﻿/*
 * KLF Headless Browser
 * Written by Kris Oye <kristianoye@gmail.com>
 * Copyright (C) 2011-2012.  All rights reserved.
 */
namespace KLF.Lib.Browser.DOM.HTML
{
    #region Using Statements

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    #endregion

    public class HTMLTableElement : HTMLElement
    {
        #region Constructors

        internal HTMLTableElement(Document owner)
            : base(owner, HTMLConstants.HTML_TAG_TABLE)
        {
        }

        #endregion

        #region DOM Level 2 HTML Compliance

        /// <summary>
        /// 
        /// </summary>
        [Obsolete]
        public string bgColor
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_BGCOLOR);
            }
            set
            {
                setAttribute(HTMLConstants.HTML_ATT_BGCOLOR, value);
            }
        }

        /// <summary>
        /// The width of the border around the table. 
        /// See the border attribute definition in HTML 4.01.
        /// </summary>
        public string border
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_BORDER);
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_BORDER);
                else
                    setAttribute(HTMLConstants.HTML_ATT_BORDER, value);
            }
        }

        /// <summary>
        /// Specifies the horizontal and vertical space between cell content 
        /// and cell borders. See the cellpadding attribute definition 
        /// in HTML 4.01.
        /// </summary>
        public string cellPadding
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_CELLPADDING);
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_CELLPADDING);
                else
                    setAttribute(HTMLConstants.HTML_ATT_CELLPADDING, value);
            }
        }

        /// <summary>
        /// Specifies the horizontal and vertical separation between cells. See 
        /// the cellspacing attribute definition in HTML 4.01.
        /// </summary>
        public string cellSpacing
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_CELLSPACING);
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_CELLSPACING);
                else
                    setAttribute(HTMLConstants.HTML_ATT_CELLSPACING, value);
            }
        }

        /// <summary>
        /// Returns the table's CAPTION, or void if none exists.
        /// </summary>
        public HTMLTableCaptionElement caption
        {
            get
            {
                Type captionType = typeof(HTMLTableCaptionElement);

                return (HTMLTableCaptionElement)Elements
                    .DefaultIfEmpty(null)
                    .FirstOrDefault(e => e.GetType() == captionType);
            }
            set
            {
                Type captionType = typeof(HTMLTableCaptionElement);

                HTMLTableCaptionElement existing = (HTMLTableCaptionElement)Elements
                    .DefaultIfEmpty(null)
                    .FirstOrDefault(e => e.GetType() == captionType);

                if (existing != null)
                    removeChild(existing);

                insertChild(0, value);
            }
        }

        /// <summary>
        /// Create a new table caption object or return an existing one. 
        /// </summary>
        /// <returns></returns>
        public HTMLElement createCaption()
        {
            Type captionType = typeof(HTMLTableCaptionElement);

            HTMLTableCaptionElement current = (HTMLTableCaptionElement)Elements
                .DefaultIfEmpty(null)
                .FirstOrDefault(e => e.GetType() == captionType);

            if (current == null)
            {
                current = (HTMLTableCaptionElement)Owner.createElement(HTMLConstants.HTML_TAG_TABLECAPTION);
                insertChild(0, current);
            }
            return current;
        }

        /// <summary>
        /// Create a table footer row or return an existing one. 
        /// </summary>
        /// <returns></returns>
        public HTMLTableSectionElement createTFoot()
        {
            Type rowTableSection = typeof(HTMLTableSectionElement);

            HTMLTableSectionElement foot = (HTMLTableSectionElement)Elements
                .DefaultIfEmpty(null)
                .FirstOrDefault(e =>
                    e.GetType() == rowTableSection && e.tagName.Equals(HTMLConstants.HTML_TAG_TABLEFOOT));

            if (foot == null)
            {
                foot = (HTMLTableSectionElement)Owner.createElement(HTMLConstants.HTML_TAG_TABLEFOOT);
                appendChild(foot);
            }
            return foot;
        }

        /// <summary>
        /// Create a table header row or return an existing one. 
        /// </summary>
        /// <returns></returns>
        public HTMLTableSectionElement createTHead()
        {
            Type rowTableSection = typeof(HTMLTableSectionElement);

            HTMLTableSectionElement head = (HTMLTableSectionElement)Elements
                .DefaultIfEmpty(null)
                .FirstOrDefault(e =>
                    e.GetType() == rowTableSection && e.tagName.Equals(HTMLConstants.HTML_TAG_TABLEHEAD));

            if (head == null)
            {
                head = (HTMLTableSectionElement)Owner.createElement(HTMLConstants.HTML_TAG_TABLEHEAD);
                appendChild(head);
            }
            return head;
        }

        /// <summary>
        /// Delete the table caption, if one exists. 
        /// </summary>
        public void deleteCaption()
        {
            Type captionType = typeof(HTMLTableCaptionElement);

            HTMLTableCaptionElement current = (HTMLTableCaptionElement)Elements
                .DefaultIfEmpty(null)
                .FirstOrDefault(e => e.GetType() == captionType);

            if (current != null)
            {
                current.Parent.removeChild(current);
            }
        }

        /// <summary>
        /// Delete a table row. 
        /// </summary>
        /// <param name="index">The index of the row to be deleted. This index
        /// starts from 0 and is relative to the logical order (not document 
        /// order) of all the rows contained inside the table. If the index is 
        /// -1 the last row in the table is deleted.</param>
        public void deleteRow(int index)
        {
            HTMLCollection r = logicalRows;

            if (index > -1 && index < r.length)
            {
                Node n = r.item(index);
                n.Parent.removeChild(n);
            }
        }

        /// <summary>
        /// Delete the footer from the table, if one exists.  
        /// </summary>
        public void deleteTFoot()
        {
            Type footerType = typeof(HTMLTableSectionElement);

            HTMLTableSectionElement current = (HTMLTableSectionElement)Elements
                .DefaultIfEmpty(null)
                .FirstOrDefault(e => 
                    e.GetType() == footerType && 
                    e.tagName.Equals(HTMLConstants.HTML_TAG_TABLEFOOT));

            if (current != null)
            {
                current.Parent.removeChild(current);
            }
        }

        /// <summary>
        /// Delete the header from the table, if one exists.
        /// </summary>
        public void deleteTHead()
        {
            Type headerType = typeof(HTMLTableSectionElement);

            HTMLTableSectionElement current = (HTMLTableSectionElement)Elements
                .DefaultIfEmpty(null)
                .FirstOrDefault(e =>
                    e.GetType() == headerType &&
                    e.tagName.Equals(HTMLConstants.HTML_TAG_TABLEHEAD));

            if (current != null)
            {
                current.Parent.removeChild(current);
            }
        }

        /// <summary>
        /// Specifies which external table borders to render. 
        /// See the frame attribute definition in HTML 4.01.
        /// </summary>
        public string frame
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_FRAME);
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_FRAME);
                else
                    setAttribute(HTMLConstants.HTML_ATT_FRAME, value);
            }
        }

        /// <summary>
        /// Insert a new empty row in the table. The new row is inserted 
        /// immediately before and in the same section as the current indexth
        /// row in the table. If index is -1 or equal to the number of rows, 
        /// the new row is appended. In addition, when the table is empty the 
        /// row is inserted into a TBODY which is created and inserted into 
        /// the table.
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public HTMLElement insertRow(int index)
        {
            HTMLTableRowElement row = (HTMLTableRowElement)Owner.createElement(HTMLConstants.HTML_TAG_TABLEROW);
            HTMLCollection existing = logicalRows;

            if (existing.length == 0)
            {
                HTMLTableSectionElement tbody = (HTMLTableSectionElement)Owner.createElement(HTMLConstants.HTML_TAG_TABLEBODY);
                tbody.appendChild(row);
                appendChild(tbody);
            }
            else if (index == -1 || index == existing.length)
            {
                appendChild(row);
            }
            else
            {
                Node n = existing.item(index);
                n.Parent.insertBefore(row, n);
            }

            return row;
        }

        /// <summary>
        /// Returns the last tbody element in the table (non-standard)
        /// </summary>
        public HTMLTableSectionElement lastBodyElement
        {
            get
            {
                HTMLCollection c = tBodies;

                if (tBodies.length == 0)
                    return null;

                return (HTMLTableSectionElement)tBodies.Nodes.Last();
            }
        }

        /// <summary>
        /// Custom attribute -- returns rows in logical order according
        /// to THEAD, TBODY, TFOOT ordering.
        /// </summary>
        public HTMLCollection logicalRows
        {
            get
            {
                List<Node> returnValue = new List<Node>();
                HTMLTableSectionElement head = tHead;
                HTMLCollection bodies = tBodies;
                HTMLTableSectionElement foot = tFoot;

                if (head != null)
                    returnValue.AddRange(head.rows.Nodes);
                for (int i = 0; i < bodies.length; i++)
                {
                    HTMLTableSectionElement body = bodies.item(i) as HTMLTableSectionElement;

                    if (body != null)
                        returnValue.AddRange(body.rows.Nodes);
                }
                if (foot != null)
                    returnValue.AddRange(foot.rows.Nodes);

                return new HTMLCollection(Owner, returnValue);
            }
        }

        /// <summary>
        /// Returns a collection of all the rows in the table, including all in 
        /// THEAD, TFOOT, all TBODY elements.
        /// </summary>
        public HTMLCollection rows
        {
            get
            {
                Type rowType = typeof(HTMLTableRowElement);
                Type sectionType = typeof(HTMLTableSectionElement);

                return new HTMLCollection(Owner, Elements
                    .Where(e => e.GetType() == rowType || e.GetType() == sectionType)
                    .Cast<Node>());
            }
        }

        /// <summary>
        /// Specifies which internal table borders to render. See the rules 
        /// attribute definition in HTML 4.01.
        /// </summary>
        public string rules
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_RULES);
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_RULES);
                else
                    setAttribute(HTMLConstants.HTML_ATT_RULES, value);
            }
        }

        /// <summary>
        /// Description about the purpose or structure of a table. 
        /// See the summary attribute definition in HTML 4.01.
        /// </summary>
        public string summary
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_SUMMARY);
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                    removeAttribute(HTMLConstants.HTML_ATT_SUMMARY);
                else
                    setAttribute(HTMLConstants.HTML_ATT_SUMMARY, value);
            }
        }

        /// <summary>
        /// Returns a collection of the table bodies (including implicit ones).
        /// </summary>
        public HTMLCollection tBodies
        {
            get
            {
                Type rowTableSection = typeof(HTMLTableSectionElement);

                return new HTMLCollection(Owner, Elements
                    .Where(e => 
                        e.GetType() == rowTableSection || 
                        e.tagName.Equals(HTMLConstants.HTML_TAG_TABLEBODY))
                    .Cast<Node>());
            }
        }
        
        /// <summary>
        /// Returns the table's TFOOT, or null if none exists.
        /// </summary>
        public HTMLTableSectionElement tFoot
        {
            get
            {
                Type rowTableSection = typeof(HTMLTableSectionElement);

                return (HTMLTableSectionElement)Elements
                    .DefaultIfEmpty(null)
                    .FirstOrDefault(e =>
                        e.GetType() == rowTableSection && e.tagName.Equals(HTMLConstants.HTML_TAG_TABLEFOOT));
            }
            set
            {
                Type sectionType = typeof(HTMLTableSectionElement);

                HTMLTableSectionElement existing = (HTMLTableSectionElement)Elements
                    .DefaultIfEmpty(null)
                    .FirstOrDefault(e => e.GetType() == sectionType && e.tagName.Equals(HTMLConstants.HTML_TAG_TABLEFOOT));

                if (existing != null)
                    existing.Parent.removeChild(existing);

                appendChild(value);
            }
        }
        /// <summary>
        /// Returns the table's THEAD, or null if none exists.
        /// </summary>
        public HTMLTableSectionElement tHead
        {
            get
            {
                Type rowTableSection = typeof(HTMLTableSectionElement);

                return (HTMLTableSectionElement)Elements
                    .DefaultIfEmpty(null)
                    .FirstOrDefault(e =>
                        e.GetType() == rowTableSection && e.tagName.Equals(HTMLConstants.HTML_TAG_TABLEHEAD));
            }
            set
            {
                Type sectionType = typeof(HTMLTableSectionElement);

                HTMLTableSectionElement existing = (HTMLTableSectionElement)Elements
                    .DefaultIfEmpty(null)
                    .FirstOrDefault(e => e.GetType() == sectionType && e.tagName.Equals(HTMLConstants.HTML_TAG_TABLEHEAD));

                if (existing != null)
                    existing.Parent.removeChild(existing);

                insertChild(0, value);
            }
        }

        /// <summary>
        /// Specifies the desired table width. See the width attribute 
        /// definition in HTML 4.01.
        /// </summary>
        public string width
        {
            get
            {
                return getAttribute(HTMLConstants.HTML_ATT_WIDTH);
            }
            set
            {
                setAttribute(HTMLConstants.HTML_ATT_WIDTH, value);
            }
        }

        #endregion
    }
}
